package wortubung.lang.de;

import static wortubung.lang.ConjugatorBase.getRomanString;
import static wortubung.lang.ConjugatorBase.getRomanStringArray;
import static wortubung.lang.de.DeutschSpracheWerkzeug.*;
import static wortubung.util.CommonUtils.*;
import static wortubung.util.JSONUtils.*;
import static java.lang.Math.max;

import java.util.Collection;
import java.util.HashMap;

import org.json.JSONObject;

import wortubung.WortUbungException;
import wortubung.core.LANG;
import wortubung.core.POS;
import wortubung.inflection.CONJUGATION;
import wortubung.inflection.ConjugationResult;
import wortubung.inflection.ConjugationSet;
import wortubung.inflection.UnknownConjugationException;
import wortubung.inflection.VerbConjugation;
import wortubung.util.ImmutableStringList;
import wortubung.util.Printable;

public class StarkeVerbKonjugierer /*extends Konjugierer*/ implements VerbConjugation {

	static final ImmutableStringList STRONG_SIMPLE_PAST_INDICES = isl("", "st", "", "en", "t", "en");

	protected String verb;
	protected boolean isSuffixOnly;
	protected String bedeutung;
	protected String partizip;
	protected String partizipPrasensOverride = ___;
	protected String hilfsverb;
	protected String erganzung;
	protected String[] s3Varianten; // [0]: present; [1]: simple past

	protected HashMap<CONJUGATION,String[]> overrides;
	// "_" means skip override. String[0] means remove.

	protected VerbConjugation alternativ;

	public StarkeVerbKonjugierer() {}

	private StarkeVerbKonjugierer(
			String verb,
			boolean suffixOnly,
			String partizip,
			String hilfsverb,
			String erganzung,
			String s3Present,
			String s3Past)
	{
		this.verb = verb;
		this.isSuffixOnly = suffixOnly;
		this.partizip = partizip;
		this.partizipPrasensOverride = ___;
		this.hilfsverb = hilfsverb;
		this.erganzung = erganzung;
		this.s3Varianten = new String[]{ s3Present, s3Past };
	}

	public StarkeVerbKonjugierer deepCopy() {
		StarkeVerbKonjugierer ret = new StarkeVerbKonjugierer(verb, isSuffixOnly, partizip,
				hilfsverb, erganzung, at(s3Varianten, 0), at (s3Varianten, 1));
		ret.bedeutung = this.bedeutung;

		if (this.overrides != null) {
			ret.overrides = new HashMap<CONJUGATION,String[]>();
			for (CONJUGATION conj : this.overrides.keySet())
				ret.overrides.put(conj, this.overrides.get(conj));
		}
		return ret;
	}

	@Override
	public ConjugationResult getConjugationResult(LANG langForLatinizing) {
		ConjugationResult ret = conjugate_(verb).getConjugationResult(langForLatinizing);
		ret.setRefIndex1(getRefIndex1());
		ret.setRefIndex2(getRefIndex2());

		ret.getConjugationResult(langForLatinizing).getConjugations().get(0).override(overrides);

		if (alternativ != null) {
			ConjugationResult alt = alternativ.getConjugationResult(langForLatinizing);
			ret.getConjugations().addAll(alt.getConjugations());
		}
		return ret;
	}

	private VerbConjugation conjugate_(String verbSuffix) {
		ConjugationResult ret = new ConjugationResult(verbSuffix);

		ConjugationSet cs = new ConjugationSet(ret, participle(verbSuffix, true), participle(verbSuffix, false));
		for (CONJUGATION type : CONJUGATION.forLanguage(LANG.de)) {
			String[] conj = conjugate(verbSuffix, type);
			if (conj != null)
				cs.add(type, conj);
		}
		ret.addConjugationSet(cs);
		return ret;
	}

	public void setAlternative(VerbConjugation alternativ) { this.alternativ = alternativ; }

	public String[] conjugate(String verbSuffix, CONJUGATION type) {
		String[] sa;
		String s3;
		if (s3Varianten != null) {
			switch (type) {
			case PRESENT:
				s3 = s3Varianten[0];
				if (___.equals(s3))
					break;
				sa = Konjugierer.defaultKonjugierer.conjugate(verbSuffix, type, false);
				sa[2] = s3;
				if (s3.endsWith("st")) {
					sa[1] = s3;
				}
				else if (s3.endsWith("t")) {
					s3 = chopRight(s3, 1);
					if (s3.endsWith("s"))
						sa[1] = s3 + "est";
					else
						sa[1] = s3 + "st";
				}
				else if (s3.endsWith("s")) { // e.g. 'wissen' (s3.present: 'weiss')
					sa[0] = s3;
					sa[1] = s3 + 't';
				}
				else {
					sa[1] = s3 + "st";
				}
				return sa;
			case SIMPLE_PAST:
				if (s3Varianten.length > 1)
					return appendWithExtraE(s3Varianten[1], STRONG_SIMPLE_PAST_INDICES, POS.VERB);
				break;
			case SUBJ_PAST:
				if (s3Varianten.length > 1)
					return appendWithExtraE(addFirstUmlaut(s3Varianten[1]), Konjugierer.SUBJ_PRESENT_INDICES, POS.VERB);
				break;
			}
		}
		return Konjugierer.defaultKonjugierer.conjugate(verbSuffix, type, false);
	}

	protected String participle(String verbSuffix, boolean isPresent) {
		if (isPresent) {
			if (!___.equals(partizipPrasensOverride))
				return partizipPrasensOverride;
			return Konjugierer.defaultKonjugierer.participle(verbSuffix, true);
		}

		if (isNotEmpty(partizip))
			return partizip;

		// default strong verb (without prefix) past participle
		return "ge" + verbSuffix;
	}

	@Override public String getVerb() { return verb; }
	@Override public String getPrefix() { return null; }
	@Override public String getDerivedFrom() { return null; }
	@Override public boolean hasAlternative() { return false; }
	@Override public boolean isSuffixOnly() { return isSuffixOnly; }
	@Override public boolean isRootSuffixOnly() { return false; }
	@Override public Integer getRefIndex1() { return null; }
	@Override public Integer getRefIndex2() { return null; }
	@Override public String getMeaning() { return bedeutung; }

	@Override public String[] getUsage() {
		if (hilfsverb == null && erganzung == null)
			return null;
		if (erganzung == null)
			return new String[]{ hilfsverb };
		return new String[]{ hilfsverb, erganzung };
	}

	/**
	 * @param line The format: "(&lt;single&gt;|"_") "," [&lt;plural&gt;] [ ";" &lt;partizip&gt; ],
	 *        or simply "?" indicating it is so irregular that must be defined separately. 
	 *        c.f. 'Konjugationen-by-rule.de.json'.
	 */
	public static StarkeVerbKonjugierer parseVerbInflection(String verb, String line) {
		line = line.trim();
		if (line.startsWith("?")) {
			// It is too irregular it can't be covered here.
			// Look up the special conjugator; if not found, fail it.
			VerbConjugation vc = Konjugierer.defaultKonjugierer.getIrregulerConjugator(verb);
			if (vc == null)
				UnknownConjugationException.badVerb("Special conjugation for '" + verb + "' is not available.");
		}

		StarkeVerbKonjugierer svk = new StarkeVerbKonjugierer();
		svk.verb = verb;
		String[] sa = line.split(";");
		if (sa.length > 1) {
			svk.partizip = sa[1].trim();
			line = sa[0];
		}
		svk.s3Varianten = line.split(",");
		trim(svk.s3Varianten);
		if (isEmpty(svk.s3Varianten))
			svk.s3Varianten = null;

		Konjugierer.defaultKonjugierer.addIrregulerConjugation(svk);
		return svk;
	}

	// JSON data

	public static final String VERB         = "VERB";
	public static final String SUFFIX       = "SUFFIX";
	public static final String PARTIZIP     = "PARTIZIP";
	public static final String PARTIZIPIEN  = "PARTIZIPIEN";
	public static final String GEBRAUCH     = "GEBRAUCH";
	public static final String BEDEUTUNG    = "BEDEUTUNG";
	public static final String S3_VARIANTEN = "S3_VARIANTEN";
	public static final String OVERRIDE     = "OVERRIDE";
	public static final String ALTERNATIV   = "ALTERNATIV";
	public static final String COPY         = "COPY";

	public static StarkeVerbKonjugierer getRuleFromJSON(JSONObject json) {
		return ruleFromJSON(json, null);
	}

	private static StarkeVerbKonjugierer ruleFromJSON(JSONObject json, StarkeVerbKonjugierer precedingSibling) {
		StarkeVerbKonjugierer svk;
		String[] sa;

		if (precedingSibling != null && getBoolean(json,COPY)) {
			svk = precedingSibling.deepCopy();
		} else {
			svk = new StarkeVerbKonjugierer();
			svk.verb = getRomanString(json, VERB, null);
			if (svk.verb == null) {
				svk.verb = getRomanString(json, SUFFIX, null);
				svk.isSuffixOnly = true;
			}
			if (svk.verb == null)
				return null; // let it fail.
			svk.partizip = getRomanString(json, PARTIZIP, null);
			svk.bedeutung = getRomanString(json, BEDEUTUNG, null);
			sa = getRomanStringArray(json, GEBRAUCH);
			svk.hilfsverb = at(sa, 0);
			svk.erganzung = at(sa, 1);
			svk.s3Varianten = getRomanStringArray(json, S3_VARIANTEN);
			if (isEmpty(svk.s3Varianten))
				svk.s3Varianten = null;
			if (json.has(PARTIZIPIEN)) {
				if (json.has(PARTIZIP))
					throw new WortUbungException("Verb '" + svk.verb + "' has both " + PARTIZIPIEN + " and " + PARTIZIP + " set.");
				sa = getRomanStringArray(json, PARTIZIPIEN);
				if (sa.length > 0)
					svk.partizipPrasensOverride = sa[0];
				if (sa.length > 1)
					svk.partizip = sa[1];
			}
		}

		// read override
		if (json.has(OVERRIDE)) {
			if (svk.overrides == null)
				svk.overrides = new HashMap<CONJUGATION,String[]>();
			JSONObject jsonOver = json.getJSONObject(OVERRIDE);
			for (Object key : jsonOver.keySet()) {
				sa = getRomanStringArray(jsonOver, key.toString());
				svk.overrides.put(CONJUGATION.valueOf(key.toString()), sa);
			}
		}

		// read alternativ
		if (json.has(ALTERNATIV))
			svk.setAlternative(ruleFromJSON(json.getJSONObject(ALTERNATIV), svk));

		return svk;
	}

	public static void rulesToJSON(Collection<StarkeVerbKonjugierer> verbs, Printable prn) {
		int labelLen = S3_VARIANTEN.length();
		int verbLen     = 5;
		int gebrauchLen = 5;
		String verb;
		String gebrauch;
		for (StarkeVerbKonjugierer svk : verbs) {
			verb = qs(svk.verb);
			if (svk.hilfsverb != null || svk.erganzung != null) {
				gebrauch = "[ " + qs(svk.hilfsverb) + ", " + qs(svk.erganzung) + " ],";
				if (gebrauch.length() > gebrauchLen)
					gebrauchLen = gebrauch.length();
			}
			if (verb.length() > verbLen)
				verbLen = verb.length();
		}
		
		verbLen += 2;
		gebrauchLen += GEBRAUCH.length() + 2;

		String fmt1 = "{%" + labelLen + "s: %-" + verbLen + "s %-" + gebrauchLen + "s " + BEDEUTUNG + ": %s,\n";
		String fmt2 = " %" + labelLen + "s: %s,\n";
		String fmt3 = " %" + labelLen + "s  %s,\n";
	
		prn.println("[\n");
		for (StarkeVerbKonjugierer svk : verbs) {
			verb = qs(svk.verb) + ',';
			if (svk.hilfsverb != null || svk.erganzung != null) {
				gebrauch = GEBRAUCH + ": " + toJSONArray(svk.getUsage(), true) + ",";
			} else {
				gebrauch = "";
			}
			prn.printf(fmt1, VERB, verb, gebrauch, qs(svk.bedeutung));
			if (svk.partizip != null && !svk.partizip.equals("ge" + svk.verb))
				prn.printf(fmt2, PARTIZIP, qs(svk.partizip));
			if (isNotEmpty(svk.s3Varianten))
				prn.printf(fmt2,  S3_VARIANTEN, toJSONArray(svk.s3Varianten, true));
			if (svk.overrides != null) {
				if (svk.overrides.size() == 1) {
					for (CONJUGATION conj : svk.overrides.keySet())
						prn.printf(fmt2, OVERRIDE, "{ " + conj.name() + ": " + toJSONArray(svk.overrides.get(conj), true) + " },");
				} else {
					boolean first = true;
					int titleLen = max(0, CONJUGATION.maxNameLength(LANG.de)) + 1;
					String fmtx = "%-" + titleLen + 's';
					for (CONJUGATION conj : svk.overrides.keySet()) {
						String conjData = String.format(fmtx, conj.name() + ':') + " " + toJSONArray(svk.overrides.get(conj), true) + ",";
						if (first) {
							prn.printf(fmt2, OVERRIDE, "{ " + conjData);
							first = false;
						} else {
							prn.printf(fmt3,  "", "  " + conjData);
						}
					}
					prn.printf(fmt2,  "", "}");
				}
			}
			prn.println("},");
		}
		prn.println("\n]");
	}
}
